#include <stdio.h>
#include "ctrl.h"
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/sendfile.h>
#include <signal.h>
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>
#include "configure.h"
#include "url_info.h"
#include "file_answer.h"
#include "disk_answer.h"
#include "file_mgr.h"
#include "stat.h"
#include "admin.h"
#include "cache.h"
//#define MSGLOG

#define ALARM_VALUE 60*20 // 20 minuts
#define EXPIRE_TIME 60*15
#define MYRAND(s) ((u_int)((s*1103515245+12345)/65536) % 32768)

extern char buff_404[]; 
extern int buff_404_len;
time_t last_send_time = 0;

void daemon_chg();
void send_data(int fid, int socket_id, unsigned int from, unsigned int to, int half, int totallen, char *content_type);
int dir_path(char *file_name, char *dir_name, struct conf *conf);
inline void send_404(int sock);
size_t get_head( void *ptr, size_t size, size_t nmemb, void *userdata);
inline int file_exist(char *pathname);
void* http_recei_requst(int socket_id);
int create_dir_if_not_exist(char *pathname);
int create_relative_path(struct url_info *url);
void openlog();
inline void closelog();
void msglog(char *msg);
char *file_path;
#define N_CONF 12
#define CONF_UPDATE_INTEVAL (60*5)
struct conf confs[N_CONF] = {{0,}, };
struct conf *gconf = &confs[0];
void* update_conf(void* parm)
{
	u_int i = 0;
	while (1)
	{
		sleep(CONF_UPDATE_INTEVAL);
		i = (i+1)%N_CONF;
		free_config(&confs[i]);
		read_config_file(&confs[i]);
		gconf = &confs[i];
	}
}
void create_update_conf_thread()
{
	pthread_t t;
	if (pthread_create(&t, 0, update_conf, 0) != 0)
	{
		perror("pthread_create");
		_exit(0);
	}
}
inline void handler(int i)
{
	if (time(0)-last_send_time > EXPIRE_TIME)
		_exit(0);
}
void start_cleaner_task()
{
	int i;
	struct conf *conf = gconf;
	for(i=0; i<conf->nroot_dir; i++)
	{
		if(fork() == 0){   
			while(1){
				clean_disk(conf->root_dir[i]);
				sleep(10);
			}
			_exit(0);
		}
	}
}
#define SHM_KEY 99
#define SHM_SIZE sizeof(struct cache_stat)
struct cache_stat *cache_stat;
void create_share_stat()
{
	int shmid;
	char *shmptr;

	if ((shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT)) == -1 )
	{
		perror("shmget");
		_exit(0);
	}

	if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1 )
	{
		perror("shmat");
		_exit(0);
	}

	cache_stat = (struct cache_stat*)shmptr;
	if (stat_init(cache_stat) < 0)
	{
		printf("stat_init error\n");
		_exit(0);
	}
}

//#define PRINT
int main(void)
{
	int on = 1;
	int sock_lsn, sock_peer, pri=0;
	struct sockaddr_in addr_lsn;

	printf("main pid: %d\n", getpid());
#ifdef PRINT
	pri = 1;
#endif
	if(!pri) daemon_chg();
	signal(SIGPIPE, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	umask(0);
	buff_404_len = strlen(buff_404);
	read_config_file(&confs[0]);
	create_update_conf_thread();
#ifdef MSGLOG
	openlog();
#endif
	memset(&addr_lsn, 0, sizeof(addr_lsn));
	addr_lsn.sin_family = AF_INET;
	addr_lsn.sin_port = htons(80);
	addr_lsn.sin_addr.s_addr = htonl(INADDR_ANY);
	if ((sock_lsn = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket() error\n");
		return 0;
	}
	setsockopt(sock_lsn, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
	if (bind(sock_lsn, (struct sockaddr*)&addr_lsn, (socklen_t)sizeof(addr_lsn)) < 0)
	{
		printf("bind() fail\n");
		return 0;
	}

	if (listen(sock_lsn, 20) < 0)
	{
		printf("listen error\n");
		return 0;
	}

	/* create_share_stat() must be called before others
	 * task are created */
	create_share_stat();
	start_file_answer();
	start_disk_answer();
	start_cleaner_task();
	start_admin();
	while (1)
	{
		if ((sock_peer = accept(sock_lsn, 0, 0)) < 0)
		{
			continue;
		}
		setsockopt(sock_peer, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
		if (fork() == 0)
		{
			signal(SIGALRM, handler);
			alarm(ALARM_VALUE);
			close(sock_lsn);
			http_recei_requst(sock_peer);
			close(sock_peer);
#ifdef MSGLOG
			closelog();
#endif
			_exit(0);
		} else
		{
			close(sock_peer);
		}
	}
	return 0;
}

int msgfid;
void openlog()
{
	msgfid = open("/log", O_WRONLY | O_CREAT | O_APPEND);
}
inline void closelog()
{
	if (msgfid >= 0)
		close(msgfid);
}
void msglog(char *msg)
{
	if (msgfid >= 0)
		write(msgfid, msg, strlen(msg));
}

struct curl_slist* make_slist(struct url_info *url_info, struct curl_slist *slist)
{
	int i;
	for (i=0; i<url_info->hnum; i++)
		slist = curl_slist_append(slist, url_info->hline[i]);
	return slist;
}
int download_data(struct url_info *url_info, int socket_id, struct conf *conf)
{
	FILE *f;
	CURL *curl;
	CURLcode res;
	struct curlblock blk;
	char *file_path = url_info->file_path;
	time_t stime, etime;

	if (create_dir_if_not_exist(file_path) < 0)
	{
		printf("create dir error\n");
		return 0;
	}

	if (!(f = fopen(file_path, "w+")))
	{
		return 0;
	}

	if (!(curl=curl_easy_init()))
	{
		fclose(f);
		return 0;
	}
	conf->slist = make_slist(url_info, conf->slist);

	blk.sock = socket_id;
	blk.flag = 0;
	blk.f = f;
	blk.url = url_info;

	curl_easy_setopt(curl, CURLOPT_URL,url_info->url);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
	curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blk);
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_head);
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, conf->slist);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3);

	stime = time(0);
	res=curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	etime = time(0);

	if (res)
	{
		fclose(f);
		remove(file_path);
#ifdef MSGLOG
		msglog("-");
		msglog(url_info->url);
		msglog("\n");
#endif
	} else 
	{
		fclose(f);
	}

	stat_lock(cache_stat);
	if (res)
	{
		cache_stat->fail ++;
		cache_stat->item[cache_stat->index].success = 0;
	} else 
	{
		cache_stat->success ++;
		cache_stat->item[cache_stat->index].success = 1;
	}
	cache_stat->item[cache_stat->index].size = blk.file_len;
	cache_stat->item[cache_stat->index].time = etime - stime;
	strcpy(cache_stat->item[cache_stat->index].url, url_info->url);

	cache_stat->index = (cache_stat->index + 1) % MAX_ENTRY;
	stat_unlock(cache_stat);

	return !res;
}

inline int file_exist(char *pathname)
{
	return access(pathname, F_OK) == 0 ? 1 : 0;
}

int create_dir_if_not_exist(char *pathname)
{
	int i;
	char curpath[1500];
	int curpathlen = 0;
	for (i=0; pathname[i]; i++)
	{
		curpath[curpathlen++] = pathname[i];
		if (pathname[i] == '/')
		{
			curpath[curpathlen] = '\0';
			if (access(curpath, F_OK) == -1)
			{
				if (mkdir(curpath, 0777) == -1)
				{
					printf("dir: %s\n", curpath);
					return -1;
				}
			}
		}
	}
	return 1;
}

int get_range(char *range, unsigned int *from, unsigned int *to, unsigned int file_len)
{
	*from = *to = 0;
	int k = 0;
	if (!range)
	{
		*from = 0;
		*to = file_len;
		return 1;
	}
	if (sscanf(range, "Range: bytes=-%u", to) < 1 &&
			(k = sscanf(range, "Range: bytes=%u-%u", from, to)) < 1)
		return -1;
	if (*from == 0 && k < 2)
	{
		if (*to > file_len)
			return -1;
		*from = file_len - *to;
		*to = file_len;
	} else
	{
		*to = *to ? (*to+1) : file_len;
		if (*from >= *to)
			return -1;
		if (*to > file_len)
			return -1;
	}
	return 1;
}

int file_fresh(struct stat *st)
{
	return time(0) - st->st_mtime < 60*20;
}

int should_omit(char *para, struct conf *conf)
{
	int i;
	for (i=0; i<conf->nkey; i++)
		if (strstr(para, conf->key[i]))
			return 1;
	return 0;
}
int url_parameter_filter(char *para_str, char *dst, struct conf *conf)
{
	u_int len;
	u_int i;
	char *para[50];
	u_int npara;
	npara = split_r(para, para_str, "&");
	for (i=0; i<npara; i++)
	{
		if (!should_omit(para[i], conf))
		{
			len = strlen(para[i]);
			memmove(dst, para[i], len);
			dst[len] = '&';
			dst += len+1;
		}
	}
	*(dst-1) = 0;

	return 1;
}

// return prefix len deleted, 0 meaning nothing deleted
u_int del_prefix(struct url_info *url_info, struct conf *conf)
{
	u_int ret = 0;
	u_int filename_len = url_info->filenamelen;
	char *filename = url_info->filename;

	if (conf->prefix_len == 0 || filename_len <= conf->prefix_len)
		return ret;

	if (strncmp(filename, conf->buffprefix, conf->prefix_len) == 0)
	{
		memmove(filename, filename+conf->prefix_len, filename_len-conf->prefix_len+1);
		url_info->filenamelen -= conf->prefix_len;
		return conf->prefix_len;
	}

	return ret;
}
int rebuild_url(struct url_info *url_info, struct conf *conf)
{
	u_int del_len;
	int ret = 1;
	char *old_mark;

	del_len = del_prefix(url_info, conf);
	if (url_info->qmark)
	{ 
		old_mark = url_info->qmark;
		url_info->qmark -= del_len;
		ret = url_parameter_filter(old_mark+1, url_info->qmark+1, conf);
		if (*url_info->qmark == 0)
			url_info->qmark = 0;
	}
	return ret;
}

char *get_extname(char *file_name)
{
	char *loc;

	if (loc = rindex(file_name, '.'))
	{
		return loc+1;
	}

	return 0;
}

// never fail
char *default_content_type = "application/octet-stream";
char *extname_to_content_type(char *extname, struct conf *conf)
{
	int i;
	if (!extname)
		return default_content_type;
	for (i=0; i<conf->ncontyp; i++)
	{
		if (strcmp(conf->contyp_sp[i][0], extname) == 0)
			return conf->contyp_sp[i][1];
	}
	return default_content_type;
}

void create_content_type(struct url_info *url_info, struct conf *conf)
{
	if (url_info->qmark)
		*url_info->qmark = 0;

	if (url_info->filename)
		url_info->content_type = extname_to_content_type(get_extname(url_info->filename), conf);
	else 
		url_info->content_type = extname_to_content_type(0, conf);

	if (url_info->qmark)
		*url_info->qmark = '?';
}

int range_satisfied(struct url_info *url_info, struct stat *st)
{
	return url_info->from+sizeof(int) < st->st_size;
}

int relative_path_exist(struct url_info *url_info, u_int rnum, struct conf *conf)
{
	int i, ret = 0;
	u_int n;
	char *file_path;
	for (i=0; i<conf->nroot_dir; i++)
	{
		n = (rnum+i)%conf->nroot_dir;
		url_info->file_path = url_info->file_path_relative - conf->root_dir_len[n];
		memcpy(url_info->file_path, conf->root_dir[n], conf->root_dir_len[n]);
		if (file_exist(url_info->file_path))
			return 1;
	}
	return ret;
}

void assign_path_prefix(struct url_info *url_info, u_int rnum, struct conf *conf)
{
	u_int n = conf->ava_root_dir[rnum%conf->nava];
	url_info->file_path = url_info->file_path_relative - conf->root_dir_len[n];
	memcpy(url_info->file_path, conf->root_dir[n], conf->root_dir_len[n]);
}

int create_full_path(struct url_info *url_info, struct conf *conf)
{
	int ret;
	char *url;
	char *loc, *loc2;

	if (url_info->qmark)
		*url_info->qmark = 0;

	url_info->file_path = url_info->path_buff;

	if (url_info->filename)
		ret = dir_path(url_info->filename, url_info->file_path, conf);
	else
	{
		url = url_info->url + 7;
		if (!(loc = index(url, '/')))
			return -1;
		*loc = 0;
		ret = dir_path(url, url_info->file_path, conf);
		*loc = '/';
	}

	if (url_info->qmark)
		*url_info->qmark = '?';
	return ret;
}


struct timeval tvsnd = {10,0}; 
struct timeval tvrcv = {30,0}; 
void* http_recei_requst(int socket_id){
	struct url_info url;
	int fid = -1;
	u_int real_len;
	struct stat st;
	unsigned int from, to;
	u_int rnum;
	u_int exist;

	if (setsockopt(socket_id, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt");

	if (setsockopt(socket_id, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt");

	if (recv_header(socket_id, &url) < 0)
	{
		send_404(socket_id);
		goto out;
	}

	if (process_header_line(&url) < 0)
	{
		send_404(socket_id);
		goto out;
	}

	if (locate_filename_and_qmark(&url) < 0)
	{
		send_404(socket_id);
		goto out;
	}

#ifdef MSGLOG
	msglog("+");
	msglog(url.url);
	msglog("\n");
#endif

	if (rebuild_url(&url, gconf) < 0)
	{
		send_404(socket_id);
		goto out;
	}

	create_content_type(&url, gconf);

	if (gconf->use_map)
	{
		if (create_full_path(&url, gconf) < 0)
		{
			send_404(socket_id);
			goto out;
		}
		exist = file_exist(url.file_path);
	} else
	{
		rnum = MYRAND(getpid())%100;
		if (create_relative_path(&url) < 0)
		{
			send_404(socket_id);
			goto out;
		}
		exist = relative_path_exist(&url, rnum, gconf);
	}

	if (exist)
	{
		struct disk_fmt disk_fmt;

		file_path = url.file_path;
		if ((fid = open(file_path, O_RDONLY)) < 0)
		{
			perror("open()");
			send_404(socket_id);
			goto out;
		}
		if (read(fid, &disk_fmt, sizeof(struct disk_fmt)) != sizeof(struct disk_fmt)) {
			send_404(socket_id);
			goto cfid;
		}
		if (disk_fmt.magic == DISK_MAGIC) {
			cache_response(socket_id, fid, &url, &disk_fmt);
			goto out; /* cache_response() will close fd */
		}

		/* old disk format */
		real_len = *(int*)&disk_fmt;
		if (fstat(fid, &st) < 0)
		{
			perror("fstat()");
			send_404(socket_id);
			goto cfid;
		}
		if (get_range(url.range, &from, &to, real_len) < 0)
		{
			send_404(socket_id);
			goto cfid;
		}
		url.from = from;
		url.to = to;

		if (real_len + sizeof(int) <= st.st_size)
		{
			send_data(fid, socket_id, from, to, url.range ? 1 : 0, real_len, url.content_type);
			stat_atom_inc(cache_stat, hit_success);
		} else
		{
			if (file_fresh(&st))
			{
				if (!url.range || range_satisfied(&url, &st))
				{
					send_data(fid, socket_id, from, to, url.range ? 1 : 0, real_len, url.content_type);
					stat_atom_inc(cache_stat, hit_success);
				} else
				{
					send_404(socket_id);
					stat_atom_inc(cache_stat, hit_404);
				}
			} else
			{
				close(fid);
				fid = -1;
				remove(file_path);
				stat_atom_inc(cache_stat, remove_scrap);
				download_data(&url, socket_id, gconf);
			}
		}
	} else
	{
		if (!gconf->use_map)
			assign_path_prefix(&url, rnum, gconf);
		download_data(&url, socket_id, gconf);
	}

cfid:
	if (fid >= 0)
		close(fid);
out:
	return 0;
}

void daemon_chg()
{
	printf("entering daemon mode...\n");
	if (daemon(0, 0) < 0)
	{
		perror("daemon");
		_exit(0);
	}
}
size_t get_head( void *ptr, size_t size, size_t nmemb, void *userdata)
{
	struct curlblock *blk = (struct curlblock*)userdata;
	FILE *f = blk->f;
	unsigned int i;
	if (strcmp((char*)ptr, "HTTP/1.1 200 OK\r\n") == 0 ||
			strcmp((char*)ptr, "HTTP/1.0 200 OK\r\n") == 0)
	{
		if (fseek(f, sizeof(struct disk_fmt), SEEK_SET) < 0) {
			return 0;
		}
		blk->flag = BLK_F_200;
		blk->head_len = 0;
		blk->file_len = 0;
		return size*nmemb;
	}

	if (strncmp((char*)ptr, "HTTP/1.1 30", 11) == 0 ||
			strncmp((char*)ptr, "HTTP/1.0 30", 11) == 0)
	{
		blk->flag = BLK_F_302;
		return size*nmemb;
	}

	if (blk->flag == 0)
		return 0;

	if (blk->flag == BLK_F_302)
		return size*nmemb;

	if (sscanf((char*)ptr, "Content-Length: %u", &i) == 1) {
		blk->file_len = i;
		return size * nmemb;
	}

	/*
	 * we will drop these response line
	 */
	if (strncasecmp((char*)ptr, "Connection: ", 12) == 0) 
		return size * nmemb;
	if (strncasecmp((char*)ptr, "Date: ", 6) == 0) 
		return size * nmemb;
	if (strncasecmp((char*)ptr, "Last-Modified: ", 15) == 0) 
		return size * nmemb;

	/* save all other response line */
	if (fwrite(ptr, size, nmemb, f) < 0)
		return 0;
	blk->head_len += size * nmemb;

	/* 
	 * if it is the end line, save the disk_fmt to hard disk and start a process
	 * to feed the client request 
	 */
	if (strcmp((char*)ptr, "\r\n") == 0)
	{
		struct disk_fmt disk_fmt;
		if (blk->file_len == 0) {
			send_404(blk->sock);
			return 0;  /* we not support trunk mode */
		}

		disk_fmt.ver = 1;
		disk_fmt.headlen = blk->head_len;
		disk_fmt.filelen = blk->file_len;
		disk_fmt.magic = DISK_MAGIC;

		if (fseek(f, 0, SEEK_SET) < 0 || 
				fwrite(&disk_fmt, sizeof(struct disk_fmt), 1, f) < 0 || 
				fflush(f) != 0 || fseek(f, sizeof(struct disk_fmt) + disk_fmt.headlen, SEEK_SET) < 0) {
			send_404(blk->sock);
			return 0;
		}

		/* 
		 * now start a new process to feed the client 
		 * but if it is range request, fail it, and 
		 * the download contineu
		 */
		if (blk->url->range) {
			send_404(blk->sock);
			return size*nmemb;
		}
		if (fork() == 0) {
			int fid = open(blk->url->file_path, O_RDONLY);
			if (fid >= 0) {
				struct dl_task dl_task;

				dl_task.url_info = blk->url;
				dl_task.disk = &disk_fmt;
				dl_task.from = 0;
				dl_task.to = disk_fmt.filelen;
				dl_task.total = disk_fmt.filelen;
				dl_task.use206 = 0;

				sleep(1);
				new_send_data(fid, blk->sock,&dl_task);
				close(fid);
				close(blk->sock);
			} else 
				send_404(blk->sock);
			_exit(0);
		}
	}
	return size*nmemb;
}

// >0 nonblocking, 0 timeout, -1 error
int nonblocking_r(int fd, int timeout)
{
	struct timeval t = {timeout, 0};
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	select(fd+1, &fds, 0, 0, &t);
	return FD_ISSET(fd, &fds);
}
int nonblocking_w(int fd, int timeout)
{
	struct timeval t = {timeout, 0};
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	select(fd+1, 0, &fds, 0, &t);
	return FD_ISSET(fd, &fds);
}
void send_data(int fid, int socket_id, unsigned int from, unsigned int to, int half, int totallen, char *content_type){ char http_head[1500] = {0};
	register int http_head_i = 0;
	register int count = 4;
	off_t start_offset;
	register size_t len=(to-from);
	if(!half)
	{
		http_head_i += sprintf(http_head+http_head_i, "HTTP/1.1 200 Ok\r\n");
		http_head_i += sprintf(http_head+http_head_i, 
				"Content-Length:%d\r\n", to);
	} else
	{
		http_head_i += sprintf(http_head+http_head_i, "HTTP/1.1 206 Partial Content\r\n");
		http_head_i += sprintf(http_head+http_head_i, 
				"Content-Range: bytes %d-%d/%d\r\n", from, to-1, totallen);
	}

	http_head_i += sprintf(http_head+http_head_i, "path: %s\r\n", file_path);
	http_head_i += sprintf(http_head+http_head_i, "Content-Type: %s\r\n", content_type);
	http_head_i += sprintf(http_head + http_head_i, "\r\n");

	from += sizeof(int);
	to += sizeof(int);
	start_offset = from;
	if (send(socket_id,http_head, http_head_i,0) < 0)
	{
		printf("head sned fail\n");
		goto out;
	}
	while(count){
		if (nonblocking_r(fid, FD_TIMEOUT) <= 0 || nonblocking_w(socket_id, FD_TIMEOUT) <= 0)
		{
			count --;
			continue;
		}
#ifdef PRINT
		printf("(%d)begin sendfile..from %d, to %d, count %d\n", getpid(), from, to, len);
#endif
		ssize_t nsend = sendfile(socket_id, fid, &start_offset, len);
#ifdef PRINT
		printf("(%d)end  sendfile..%d , %d\n", getpid(), from, to);
		if (len == nsend) printf("job done\n");
		if(nsend < 0) perror("sendfile");
#endif
		goto out;
		last_send_time = time(0);
		sleep(2);
	}
	printf("reach max count\n");
#ifdef MSGLOG
	msglog("_\n");
#endif 
out:
	return;
}

int dir_path(char *file_name, char *dir_name, struct conf *conf){
	unsigned int dir1, dir2, dir3;
	if (!file_name)
		return 0;
	dir1 = conf->map[(*(unsigned int*)file_name) % conf->nmap_str];
	dir2 = ((*(unsigned int*)file_name) & 0x0000FFFF) % 256;
	dir3 = ((*(unsigned int*)file_name) >> 16) % 256;
	sprintf(dir_name, "%s/%u/%u/%s", conf->root_dir[dir1], dir2, dir3, file_name);
	return 1;
}

int dir_path_relative(char *file_name, char *dir_name, char *host){
	unsigned int dir1, dir2, dir3;
	char* p = 0;
	char *extra = "";

	if (!file_name)
		return 0;

	dir2 = ((*(unsigned int*)file_name) & 0x0000FFFF) % 256;
	dir3 = ((*(unsigned int*)file_name) >> 16) % 256;

	if (strlen(file_name) < 8 && (p = index(host, '/'))) {
		*p = 0;
		sprintf(dir_name, "/%u/%u/%s%s", dir2, dir3, file_name, host);
		*p = '/';
	} else {
		sprintf(dir_name, "/%u/%u/%s", dir2, dir3, file_name);
	}

	return 1;
}

int create_relative_path(struct url_info *url_info)
{
	int ret;
	char *url;
	char *loc, *loc2;
	char *relative_path = url_info->file_path_relative = url_info->path_buff+MAX_PATH_PREFIX;

	if (url_info->qmark)
		*url_info->qmark = 0;

	if (url_info->filename)
		ret = dir_path_relative(url_info->filename, relative_path, url_info->url+ 7);
	else
	{
		url = url_info->url + 7;
		if (!(loc = index(url, '/')))
			return -1;
		*loc = 0;
		ret = dir_path_relative(url, relative_path, url_info->url + 7);
		*loc = '/';
	}

	if (url_info->qmark)
		*url_info->qmark = '?';
	return ret;
}
char buff_404[] = "HTTP/1.1 404 Not Found\r\n"
"Content-Length: 0\r\n"
"Connection: Close\r\n"
"\r\n";
int buff_404_len = 0; // reset in main()

inline void send_404(int sock)
{
	send(sock, buff_404, buff_404_len, 0);
}
